home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / program / p063b9s.zip / UNIT / MUSICA.PAS < prev    next >
Pascal/Delphi Source File  |  1996-04-20  |  14KB  |  492 lines

  1. (*
  2.     Musica v1.01 (c) CopyRight P.H.Rankin Hansen 1990.
  3.  
  4.     This unit implements the Play  statement knovn from Basic in Turbo
  5.     Pascal  versions  5.x  and  higher.  (version  4  does not support
  6.     procedural types). The syntax adhers  to the Basic syntax with the
  7.     exception  of the  X command,  wich has  no meaning  in a compiled
  8.     language.
  9.  
  10.     Released in Denmark on June 3rd, 1990 as part of PingAnsi 1.30.
  11.  
  12.     By  using this  material You  assume FULL  responsibility for  ANY
  13.     consequences - direct or indirect - thereof. Any dispute regarding
  14.     this  material shall  be setteled  by Danish  law and  in a Danish
  15.     Court.
  16.  
  17.      (Sigh!)
  18.  
  19.     This source  may NOT be  used by Lawyers,  Politicians or, persons
  20.     engaged  in any  other form  of terrorism.  Otherwise the usage is
  21.     free.
  22.  
  23.     This  source  may  be  freely  distributed  as  long  as no fee is
  24.     charged.
  25.  
  26.     Please direct any comments, corrections, modifications via netmail
  27.     to:
  28.  
  29.                       Ping Hansen - Fido Net 2:231/62.58
  30.  
  31. *)
  32. Unit Musica;
  33.  
  34. Interface
  35.  
  36. Uses Use32, Dos, OpCrt;
  37.  
  38. Const
  39.   MaxPlayBuffer       = 64;
  40.   { set this to true to disable background processing of sound }
  41.   NoBackground        : Boolean = False;
  42.   { If this is set stuff will WAIT for room in play buffer before returning }
  43.   WaitForSpace        : Boolean = True;
  44.  
  45. Var
  46.   BackGroundPlayHook  : Procedure(Tone, Duration : Word);
  47.   PlayBuffer          : Array[0..MaxPlayBuffer] Of
  48.     Record
  49.       Tone,
  50.       Duration            : Word;
  51.     End;
  52.  
  53. Procedure Play(St : String);
  54. Procedure PurgePlayBuffer;
  55. Function PlayBufferEmpty : Boolean;
  56. Function PlayBufferFull : Boolean;
  57. Procedure Stuff(Tone, Time : Word); far;
  58. Function GrabTimer  : Boolean;
  59. Procedure ReleaseTimer; far;
  60.  
  61.   {-----------------------------------------------------------------------}
  62.  
  63. Implementation
  64.  
  65. Const
  66.   Timer0              = 0;
  67.   FirstPlay           : Word = 0; { buffer Pointer }
  68.   LastPlay            : Word = 1; { buffer Pointer }
  69. (*
  70.   TimerMode           : Byte = 0; { saved mode for the timer }
  71. *)
  72.  
  73. Var
  74.   SaveExitProc        : Pointer;
  75.   SaveTimerInt        : Pointer;
  76.  
  77.   {-----------------------------------------------------------------------}
  78.  
  79.   Procedure Play(St : String);
  80.  
  81.   Const
  82.     Notes               : Array[1..84] Of Word =
  83.     { C    C#,D-  D    D#,E-  E     F    F#,G-  G    G#,A-  A    A#,B-  B  }
  84.     (0065, 0070, 0073, 0078, 0082, 0087, 0093, 0098, 0104, 0110, 0117, 0123,
  85.      0131, 0139, 0147, 0156, 0165, 0175, 0185, 0196, 0208, 0220, 0233, 0247,
  86.      0262, 0277, 0294, 0311, 0330, 0349, 0370, 0392, 0415, 0440, 0466, 0494,
  87.      0523, 0554, 0587, 0622, 0659, 0698, 0740, 0784, 0831, 0880, 0932, 0987,
  88.      1047, 1109, 1175, 1245, 1329, 1397, 1480, 1568, 1661, 1760, 1865, 1976,
  89.      2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
  90.      4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902);
  91.     MusicType           : Byte = 7; {Normal - note plays for 7/8 of time}
  92.     Tempo               : Word = 120; {120 beats per minute}
  93.     StdNoteLength       : Word = 4; {Quarter note}
  94.     Octave              : Word = 3; {Third octave}
  95.     BackGround          : Boolean = False; {Mn is default}
  96.  
  97.   Var
  98.     PlayTime, IdleTime,
  99.     DotTime, TempTime,
  100.     NoteLength, Note,
  101.     Index               : Word;
  102.     Ch                  : Char;
  103.  
  104.     {-------------}
  105.  
  106.     Function Numerical(Var Index : Word) : Word;
  107.  
  108.     Var
  109.       n                   : Word;
  110.     Begin
  111.       n := 0;
  112.       While (Index <= Length(St)) And (St[Index] In ['0'..'9']) Do
  113.       Begin
  114.         n := n * 10 + Ord(St[Index]) - Ord('0');
  115.         Inc(Index)
  116.       End;
  117.       Numerical := n;
  118.     End {Numerical} ;
  119.  
  120.     {-------------}
  121.  
  122.     Procedure CheckDots(Var Index : Word);
  123.  
  124.     Begin
  125.       While (Index <= Length(St)) And ((St[Index] = '.') Or (St[Index] = ',')) Do
  126.       Begin
  127.         DotTime := DotTime + DotTime Div 2;
  128.         Inc(Index)
  129.       End;
  130.     End {CheckDots} ;
  131.  
  132.     {-------------}
  133.  
  134.   Begin                           {Play subroutine}
  135.     Index := 1;
  136.     While Index < Length(St) Do
  137.     Begin
  138.       NoteLength := StdNoteLength;
  139.       DotTime := 1000;
  140.       Ch := Upcase(St[Index]);
  141.       Case Ch Of
  142.         'A'..'G' :
  143.           Begin                   {read note}
  144.             Note := Pos(Ch, 'CcDdEFfGgAaB');
  145.             Inc(Index);
  146.  
  147.             {Check for sharp or flat}
  148.             If Index <= Length(St) Then
  149.               Case St[Index] Of
  150.                 '#', '+' :
  151.                   Begin
  152.                     Inc(Note);
  153.                     Inc(Index);
  154.                   End;
  155.                 '-' :
  156.                   Begin
  157.                     Dec(Note);
  158.                     Inc(Index);
  159.                   End;
  160.               End;
  161.  
  162.             {Check for length suffix}
  163.             If (Index <= Length(St)) And
  164.             (St[Index] In ['0'..'9']) Then
  165.             Begin
  166.               NoteLength := Numerical(Index);
  167.             End;
  168.             CheckDots(Index);
  169.  
  170.             {calculate periods}
  171.             TempTime := Round(DotTime / Tempo / NoteLength * 240);
  172.             PlayTime := Round(TempTime * MusicType / 8);
  173.             IdleTime := TempTime - PlayTime;
  174.  
  175.             {Play the note}
  176.             If BackGround
  177.             Then
  178.             Begin
  179.               BackGroundPlayHook(Notes[Note + Octave * 12], PlayTime);
  180.               If IdleTime <> 0 Then BackGroundPlayHook(0, IdleTime);
  181.             End
  182.             Else
  183.             Begin
  184. {$IFDEF OS2}
  185.               PlaySound(Notes[Note + Octave * 12], PlayTime);
  186. {$ELSE}
  187.               Sound(Notes[Note + Octave * 12]);
  188.               Delay(PlayTime);
  189.               If IdleTime <> 0 Then
  190.               Begin
  191.                 NoSound;
  192.                 Delay(IdleTime)
  193.               End;
  194. {$ENDIF}
  195.             End;
  196.           End;
  197.         '<' :
  198.           Begin                   {step octave down}
  199.             If Octave > 0 Then Dec(Octave);
  200.             Inc(Index);
  201.           End;
  202.         '>' :
  203.           Begin                   {step octave up}
  204.             If Octave < 6 Then Inc(Octave);
  205.             Inc(Index);
  206.           End;
  207.         'L' :
  208.           Begin                   {set notelength}
  209.             Inc(Index);
  210.             StdNoteLength := Numerical(Index);
  211.             If (StdNoteLength < 1) Or (StdNoteLength > 64) Then
  212.               StdNoteLength := 4;
  213.           End;
  214.         'M' :
  215.           Begin                   {determine music type}
  216.             Inc(Index);
  217.             If (Index <= Length(St)) Then
  218.             Begin
  219.               Case Upcase(St[Index]) Of
  220.                 'S' : MusicType := 6; {music staccato}
  221.                 'N' : MusicType := 7; {music normal}
  222.                 'L' : MusicType := 8; {music legato}
  223.                 'B' : BackGround := True; {enable background buffering}
  224.                 'F' : BackGround := False; {disable do.}
  225.               End;
  226.               Inc(Index);
  227.             End;
  228.           End;
  229.         'O' :
  230.           Begin                   {set octave}
  231.             Inc(Index);
  232.             Octave := Numerical(Index);
  233.             If Octave > 6 Then Octave := 6;
  234.           End;
  235.         'P' :
  236.           Begin                   {pause}
  237. {$IFNDEF OS2}
  238.             NoSound;
  239. {$ENDIF}
  240.             Inc(Index);
  241.             NoteLength := Numerical(Index);
  242.             If (NoteLength < 1) Or (NoteLength > 64) Then
  243.               NoteLength := StdNoteLength;
  244.             CheckDots(Index);
  245.  
  246.             {calculate pause}
  247.             IdleTime := DotTime Div Tempo * (240 Div NoteLength);
  248.  
  249.             {execute pause}
  250.             If BackGround
  251.             Then BackGroundPlayHook(0, IdleTime)
  252.             Else Delay(IdleTime);
  253.           End;
  254.         'T' :
  255.           Begin                   {set tempo}
  256.             Inc(Index);
  257.             Tempo := Numerical(Index);
  258.             If (Tempo < 32) Or (Tempo > 255) Then
  259.               Tempo := 120;
  260.           End;
  261.         'N' :
  262.           Begin                   {play note #nn}
  263.             Inc(Index);
  264.             Note := Numerical(Index);
  265.             If (Note < 1) Then Note := 1;
  266.             If (Note > 84) Then Note := 84;
  267.             CheckDots(Index);
  268.  
  269.             {calculate periods}
  270.             TempTime := Round(DotTime / Tempo / NoteLength * 240);
  271.             PlayTime := Round(TempTime * MusicType / 8);
  272.             IdleTime := TempTime - PlayTime;
  273.  
  274.             {Play the note}
  275.             If BackGround
  276.             Then
  277.             Begin
  278.               BackGroundPlayHook(Notes[Note + Octave * 12], PlayTime);
  279.               If IdleTime <> 0 Then BackGroundPlayHook(0, IdleTime);
  280.             End
  281.             Else
  282.             Begin
  283. {$IFDEF OS2}
  284.               PlaySound(Notes[Note + Octave * 12], PlayTime);
  285. {$ELSE}
  286.               Sound(Notes[Note + Octave * 12]);
  287.               Delay(PlayTime);
  288.               If IdleTime <> 0 Then
  289.               Begin
  290.                 NoSound;
  291.                 Delay(IdleTime)
  292.               End;
  293. {$ENDIF}
  294.             End;
  295.           End;
  296.         Else                      {garbage collector}
  297.           Inc(Index);             {pollution, Just dump it}
  298.       End;
  299.     End {While} ;
  300. {$IFNDEF OS2}
  301.     NoSound;                      {we are finished}
  302. {$ENDIF}
  303.   End {Play} ;
  304.  
  305.   {-----------------------------------------------------------------------}
  306.  
  307.   Procedure DummyStuff(Tone, Duration : Word); far;
  308.     {dummy background}
  309.   Begin
  310. {$IFDEF OS2}
  311.     If Tone <> 0 Then PlaySound(Tone, Duration);
  312. {$ELSE}
  313.     If Tone <> 0 Then Sound(Tone) Else NoSound;
  314.     Delay(Duration);
  315. {$ENDIF}
  316.   End {DummyStuff} ;
  317.  
  318.   {-------------------------------------------------------------------------}
  319.  
  320.   Procedure PurgePlayBuffer;
  321.   Begin
  322. {$IFNDEF OS2}
  323.     Inline($FA); {CLI}
  324.     FillChar(PlayBuffer, SizeOf(PlayBuffer), 0);
  325.     FirstPlay := 0;
  326.     LastPlay := 1;
  327.     Inline($FB); {STI}
  328. {$ENDIF}
  329.   end {PurgePlayBuffer} ;
  330.  
  331.   {-------------------------------------------------------------------------}
  332.  
  333.   Function PlayBufferEmpty : Boolean;
  334.  
  335.   Begin
  336.     PlayBufferEmpty := (FirstPlay = LastPlay);
  337.   End {PlayBufferEmpty} ;
  338.  
  339.   {-------------------------------------------------------------------------}
  340.  
  341.   Function PlayBufferFull : Boolean;
  342.  
  343.   Begin
  344.     PlayBufferFull := (LastPlay = FirstPlay - 1) Or
  345.     ((LastPlay = MaxPlayBuffer) And (FirstPlay = 1));
  346.   End {PlayBufferFull} ;
  347.  
  348.   {-------------------------------------------------------------------------}
  349.  
  350.   Procedure Stuff(Tone, Time : Word);
  351.     { Place a note in background buffer. }
  352.   Begin
  353.     If NoBackground Then
  354.     Begin
  355. {$IFDEF OS2}
  356.       If Tone <> 0 Then PlaySound(Tone, Time);
  357. {$ELSE}
  358.       If Tone <> 0 Then Sound(Tone);
  359.       Delay(Time);
  360. {$ENDIF}
  361.       Exit;
  362.     End;
  363.     While WaitForSpace And PlayBufferFull Do {} ;
  364.     If                            {(LastPlay <> FirstPlay - 1) And
  365.     ((LastPlay <> MaxPlayBuffer) Or (FirstPlay <> 1))} Not PlayBufferFull Then
  366.     Begin
  367.       PlayBuffer[LastPlay].Tone := Tone;
  368.       PlayBuffer[LastPlay].Duration := Time;
  369.       Inc(LastPlay);
  370.       If LastPlay > MaxPlayBuffer Then LastPlay := 1;
  371.     End;
  372.   End {Stuff} ;
  373.  
  374.   {-------------------------------------------------------------------------}
  375.  
  376.   Procedure InitTimer(Timer, Mode : Byte; Count : Word);
  377. {$IFDEF OS2}
  378.   begin
  379. {$ELSE}
  380.   Var
  381.     Tics                : LongInt Absolute $40 : $6C;
  382.     t                   : LongInt;
  383.   Begin
  384.     t := Tics;
  385.     While t = Tics Do {} ;        { wait for clock tick }
  386.     Inline($FA);                  {CLI}
  387.     Port[$43] := Mode;
  388.     Port[$40 + Timer] := Lo(Count);
  389.     Port[$40 + Timer] := Hi(Count);
  390.     Inline($FB);                  {STI}
  391. {$ENDIF}
  392.   End;
  393.  
  394.   {-------------------------------------------------------------------------}
  395.  
  396. {$IFNDEF OS2}
  397.   Procedure NewTimer(BP : Word); Interrupt;
  398.   Const
  399.     InTune              : Boolean = True;
  400.     TimerVar            : Word = 54; { no delay first time }
  401.     Count               : Word = 05;
  402.   Begin
  403.     Inc(TimerVar);
  404.     If TimerVar >= 55 Then
  405.     Begin
  406.       TimerVar := 0;
  407.       Inline($9C / $FF / $1E / SaveTimerInt); { Pushf/Call Far SaveTimer }
  408.     End
  409.     Else
  410.     Begin
  411.       Port[$20] := $20;           { Non speciffic EOI }
  412.     End;
  413.     Inline($FB);                  {STI}
  414.     If Count > 0 Then Dec(Count);
  415.     If Count = 0 Then
  416.     Begin
  417.       If InTune Then
  418.       Begin
  419.         InTune := False;
  420.         NoSound;
  421.       End;
  422.       If (LastPlay <> FirstPlay) Then
  423.       Begin
  424.         If (PlayBuffer[FirstPlay].Tone <> 0) Then
  425.         Begin
  426.           Sound(PlayBuffer[FirstPlay].Tone);
  427.           InTune := True;
  428.         End;
  429.         If (PlayBuffer[FirstPlay].Duration <> 0)
  430.         Then Count := PlayBuffer[FirstPlay].Duration;
  431.         Inc(FirstPlay);
  432.         If FirstPlay > MaxPlayBuffer Then FirstPlay := 1;
  433.       End;
  434.     End;
  435.   End {NewTimer} ;
  436. {$ENDIF}
  437.  
  438.   {-------------------------------------------------------------------------}
  439.  
  440.   Procedure ReleaseTimer;
  441.     { unload the interrupt handler }
  442.   Begin
  443.     { Reprogram the 8253 to a 55 ms period }
  444.     InitTimer(Timer0, $36, 0);
  445. {$IFNDEF OS2}
  446.     SetIntVec($8, SaveTimerInt);
  447. {$ENDIF}
  448.     ExitProc := SaveExitProc;
  449. {$IFNDEF OS2}
  450.     NoSound;
  451. {$ENDIF}
  452.     BackgroundPlayHook := DummyStuff;
  453.   End {ReleaseTimer} ;
  454.  
  455.   {-------------------------------------------------------------------------}
  456.  
  457.   Function GrabTimer  : Boolean;
  458.  
  459.   Begin
  460.     GrabTimer := True;
  461.     FillChar(PlayBuffer, SizeOf(PlayBuffer), 0);
  462. {$IFNDEF OS2}
  463.     GetIntVec($8, SaveTimerInt);
  464. {$ENDIF}
  465. (*
  466.   Port[$43] := $E2;        { readback command. Timer 0, status. }
  467.   TimerMode := Port[$40] And $0F + $30;
  468.   if (TimerMode <> $36)
  469.   then GrabTimer := False
  470.   else
  471. *)
  472.     Begin
  473.       SaveExitProc := ExitProc;
  474.       InitTimer(Timer0, $36, $04A8);
  475. {$IFNDEF OS2}
  476.       SetIntVec($8, @NewTimer);
  477. {$ENDIF}
  478.       SaveExitProc := ExitProc;
  479.       ExitProc := @ReleaseTimer;
  480.       BackgroundPlayHook := Stuff;
  481.     (*
  482.     Stuff(10, 100); {void attempt to fix problem with first note}
  483.     *)
  484.     End;
  485.   End {GrabTimer} ;
  486.  
  487.   {-----------------------------------------------------------------------}
  488.  
  489. Begin
  490.   BackGroundPlayHook := DummyStuff;
  491. End.
  492.